using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ExcelTemplateEngine.Infrastructure.DependencyInjection
{
    public class SimpleServiceContainer : IServiceContainer
    {
        private readonly Dictionary<Type, ServiceDescriptor> _services = new();
        private readonly Dictionary<Type, object> _singletonInstances = new();
        private readonly object _lock = new object();
        
        public void RegisterSingleton<TInterface, TImplementation>()
            where TImplementation : class, TInterface
        {
            lock (_lock)
            {
                _services[typeof(TInterface)] = new ServiceDescriptor
                {
                    ServiceType = typeof(TInterface),
                    ImplementationType = typeof(TImplementation),
                    Lifetime = ServiceLifetime.Singleton
                };
            }
        }
        
        public void RegisterSingleton<TInterface>(TInterface instance)
        {
            lock (_lock)
            {
                _services[typeof(TInterface)] = new ServiceDescriptor
                {
                    ServiceType = typeof(TInterface),
                    Instance = instance,
                    Lifetime = ServiceLifetime.Singleton
                };
                _singletonInstances[typeof(TInterface)] = instance!;
            }
        }
        
        public void RegisterTransient<TInterface, TImplementation>()
            where TImplementation : class, TInterface
        {
            lock (_lock)
            {
                _services[typeof(TInterface)] = new ServiceDescriptor
                {
                    ServiceType = typeof(TInterface),
                    ImplementationType = typeof(TImplementation),
                    Lifetime = ServiceLifetime.Transient
                };
            }
        }
        
        public T GetService<T>()
        {
            if (TryGetService<T>(out var service))
                return service!;
                
            throw new InvalidOperationException($"Service of type {typeof(T).Name} is not registered");
        }
        
        public bool TryGetService<T>(out T? service)
        {
            service = default(T);
            
            lock (_lock)
            {
                if (!_services.TryGetValue(typeof(T), out var descriptor))
                    return false;
                
                if (descriptor.Lifetime == ServiceLifetime.Singleton)
                {
                    if (_singletonInstances.TryGetValue(typeof(T), out var instance))
                    {
                        service = (T)instance;
                        return true;
                    }
                    
                    if (descriptor.Instance != null)
                    {
                        service = (T)descriptor.Instance;
                        _singletonInstances[typeof(T)] = service!;
                        return true;
                    }
                    
                    if (descriptor.ImplementationType != null)
                    {
                        var newInstance = CreateInstance(descriptor.ImplementationType);
                        service = (T)newInstance;
                        _singletonInstances[typeof(T)] = service!;
                        return true;
                    }
                }
                else if (descriptor.Lifetime == ServiceLifetime.Transient)
                {
                    if (descriptor.ImplementationType != null)
                    {
                        service = (T)CreateInstance(descriptor.ImplementationType);
                        return true;
                    }
                }
            }
            
            return false;
        }
        
        public bool IsRegistered<T>()
        {
            lock (_lock)
            {
                return _services.ContainsKey(typeof(T));
            }
        }
        
        private object CreateInstance(Type type)
        {
            var constructors = type.GetConstructors();
            var constructor = constructors.OrderBy(c => c.GetParameters().Length).First();
            
            var parameters = constructor.GetParameters();
            var args = new object?[parameters.Length];
            
            for (int i = 0; i < parameters.Length; i++)
            {
                var paramType = parameters[i].ParameterType;
                if (_services.TryGetValue(paramType, out var paramDescriptor))
                {
                    args[i] = GetServiceByType(paramType);
                }
                else
                {
                    args[i] = null; // 或者抛出异常
                }
            }
            
            return Activator.CreateInstance(type, args)!;
        }
        
        private object? GetServiceByType(Type type)
        {
            var method = typeof(SimpleServiceContainer).GetMethod(nameof(GetService));
            var genericMethod = method!.MakeGenericMethod(type);
            return genericMethod.Invoke(this, null);
        }
        
        private class ServiceDescriptor
        {
            public Type? ServiceType { get; set; }
            public Type? ImplementationType { get; set; }
            public object? Instance { get; set; }
            public ServiceLifetime Lifetime { get; set; }
        }
    }
}